package com.hanxiaozhang.jdk8;

import org.springframework.boot.autoconfigure.security.SecurityProperties;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;


import static java.util.concurrent.Executors.newFixedThreadPool;

/**
 * 〈一句话功能简述〉<br>
 * 〈CompletableFuture的使用〉
 *
 * @author hanxinghua
 * @create 2021/11/25
 * @since 1.0.0
 */
public class No1CompletableFuture {

    public static void main(String[] args) {







        System.out.println("---- 1.runAsync()无返回值，supplyAsync()有返回值");
        // test1();

        System.out.println("---- 2.complete():如果尚未完成时，则将future.get()和相关方法返回的值设置为给定值");
        // Tips:多次设置无效
        // test2();

        System.out.println("---- 3.thenApply():对结果继续加工,转换结果,即有返回值");
        // test3();

        System.out.println("---- 4.thenAccept():对结果继续加工,消费结果,即没有返回值");
        //test4();

        System.out.println("---- 5.thenRun ():上一个任务完成后触发的回调，没有入参，也没有返回值");
        ///test5();

        System.out.println("---- 6.thenCompose():组合多个CompletableFuture，将前一个结果作为下一个计算的参数");
        // test6();

        System.out.println("---- 7.thenCombine():两个的结果进行汇总,有返回值");
        // Tips:两个任务中只要有一个执行异常，则将该异常信息作为指定任务的执行结果。
        // Tips:两个任务是并行执行的，它们之间并没有先后依赖顺序。
        // test7();

        System.out.println("---- 8.thenAcceptBoth():两个的结果进行汇总,无返回值");
        // test8();

        System.out.println("---- 9.whenComplete():计算完成后执行的回调方法,结果的异常进行处理");
        // 当CompletableFuture的任务不论是正常完成还是出现异常它都会调用whenComplete这回调函数。
        // + 正常完成：whenComplete返回结果和上级任务一致，异常为null；
        // + 出现异常：whenComplete返回结果为null，异常为上级任务的异常；
        // 即调用get()时，正常完成时就获取到结果，出现异常时就会抛出异常，需要你处理该异常。
        // test9();

        System.out.println("---- 10.exceptionally()");
        // exceptionally就像catch。
        // exceptionally的特点：
        // + 当出现异常时，会触发回调方法exceptionally
        // + exceptionally中可指定默认返回结果，如果出现异常，则返回默认的返回结果
        // test10();

        System.out.println("---- 11.handle()");
        // handle就像finally，不论正常返回还是出异常都会进入handle，类似whenComplete。
        // handle()一般接收new BiFunction<T, Throwable, R>();
        // + T：就是任务传入的对象类型
        // + Throwable：就是任务传入的异常
        // + R：就是handle自己返回的对象类型

        // handle和thenApply的区别
        // + thenApply：任务出现异常就不会进入thenApply
        // + handle：任务出现异常也会进入handle，可对异常处理

        // handle和whenComplete的区别
        // + handle对传入值进行转换，并产生自己的返回结果，T -> R
        // + whenComplete的返回值和上级任务传入的结果一致，不能对其转换
        // test11();

        System.out.println("---- 12.allOf()：多实例同时返回");
        // test12();

        System.out.println("---- 13.anyOf()：一个执行完成则往下执行");
        test13();

    }

    private static void test13() {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("one end");
            return 200;
        });
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("two end");
            return 700;
        });
        CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("three end");
            return 200;
        });

        CompletableFuture.anyOf(future1, future2, future3).join();
        System.out.println("anyOf end");
    }

    private static void test12() {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("one end");
            return 200;
        });
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("two end");
            return 700;
        });
        CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("three end");
            return 200;
        });

        CompletableFuture.allOf(future1, future2, future3).join();
        System.out.println("allOf end");
    }


    private static void test11() {
        CompletableFuture<Integer> future = CompletableFuture
                .supplyAsync(() -> "100")
                .handle(new BiFunction<String, Throwable, Integer>() {
                            @Override
                            public Integer apply(String str, Throwable throwable) {
                                if (throwable != null) {
                                    System.out.println(throwable.getMessage());
                                    return null;
                                } else {
                                    return Integer.parseInt(str);
                                }
                            }
                        }
                );
        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }


    private static void test10() {
        CompletableFuture<Double> future = CompletableFuture.supplyAsync(() -> {
            if (Math.random() < 0.5) {
                throw new RuntimeException("throw error");
            }
            System.out.println("success");
            return 1.1;
        }).thenApply(result -> {
            System.out.println("thenApply on success result is " + result);
            return result;
        }).exceptionally(new Function<Throwable, Double>() {
            @Override
            public Double apply(Throwable throwable) {
                System.out.println("error is " + throwable.getMessage());
                return 0.0;
            }
        });
        try {
            System.out.println("last is " + future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }


    private static void test9() {
        CompletableFuture.supplyAsync(() -> "hello")
                .thenApply(s -> s + " world!")
                .whenComplete((result, throwable) -> System.out.println(result));
    }

    private static void test8() {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 200);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 200);
        future1.thenAcceptBoth(future2, (f1, f2) -> {
            System.out.println(f1 + f2);
        });
    }

    private static void test7() {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 200);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 200);
        future1.thenCombine(future2, (f1, f2) -> {
            return f1 + f2;
        });
        try {
            System.out.println(future1.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    private static void test6() {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello word!")
                .thenCompose(s -> CompletableFuture.supplyAsync(() -> "thenCompose " + s));
        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    private static void test5() {
        CompletableFuture.supplyAsync(() -> "hello word!")
                .thenRun(() -> System.out.println("thenRun hello word!"));
    }

    private static void test4() {
        CompletableFuture.supplyAsync(() -> "hello word!")
                .thenAccept(s -> System.out.println("thenAccept hello word!"));
    }

    private static void test3() {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello word!")
                .thenApply(s -> "thenApply " + s);
        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    private static void test2() {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "hello word!";
        });
        future.complete("hello word1!");
        future.complete("hello word2!");
        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }


    private static void test1() {
        CompletableFuture.runAsync(() -> System.out.println("runAsync hello word!"));
        CompletableFuture.runAsync(() -> System.out.println("runAsync hello word，but use custom Executor!"),
                newFixedThreadPool(1));

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                    return "supplyAsync hello word!";
                }
        );
        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        CompletableFuture.supplyAsync(() -> {
                    System.out.println("supplyAsync hello word，but use custom Executor!");
                    return "supplyAsync hello word!";
                }, newFixedThreadPool(1)
        );

    }

}
